use core::ptr::copy_nonoverlapping;

use cmsis_rtos2_rs::static_mem::{Task, Thread};
use utils::{xor_sum, ByteBuffer, MutCell};

use crate::app::{
    config::address, driver::{cmd_can, cmd_uart::{self, ByteView}}, proto::{RequestRef, END, HEAD0, HEAD1, MIN_LEN}, service::dispatcher::DISPATCHER
};


const RECV_BYTE_INTERVAL: u32 = 20;

struct Context {
    thread: Thread<2048>,
    bw: MutCell<ByteView>,
    read_buf: ByteBuffer<255>,
}

impl Context {
    const fn uninit() -> Context {
        Self {
            thread: Thread::uninit(),
            bw: MutCell::const_new(ByteView::const_new()),
            read_buf: ByteBuffer::const_new(),
        }
    }

    fn sync(&self) -> Option<usize> {
        let mut flag = false;
        loop {
            let value = self.recv_byte()?;
            if flag && (HEAD1 == value) {
                break;
            }
            flag = value == HEAD0;
        }
        let n = self.recv_byte()?;
        Some(n as usize)
    }

    fn wait_ready(&self) {
        if self.bw.len == 0 {
            unsafe {
                *self.bw.as_mut() = cmd_uart::take();
            }
        }
    }

    fn recv_bytes(&self, buf: &mut [u8]) -> Option<()> {
        let mut index = 0;
        let len = buf.len();

        while index < len {
            if self.bw.len == 0 {
                unsafe {
                    *self.bw.as_mut() = cmd_uart::poll(RECV_BYTE_INTERVAL)?;
                }
            }

            let mut n = len - index;
            if n > self.bw.len {
                n = self.bw.len;
            }

            unsafe {
                copy_nonoverlapping(self.bw.ptr, buf.as_mut_ptr().add(index), n);
                index += n;
                self.bw.as_mut().ptr = self.bw.ptr.add(n);
                self.bw.as_mut().len -= n;
            }
        }

        Some(())
    }

    fn recv_byte(&self) -> Option<u8> {
        if self.bw.len == 0 {
            unsafe {
                *self.bw.as_mut() = cmd_uart::poll(RECV_BYTE_INTERVAL)?;
            }
        }
        unsafe {
            let value = self.bw.ptr.read();
            self.bw.as_mut().ptr = self.bw.ptr.add(1);
            self.bw.as_mut().len -= 1;
            Some(value)
        }
    }

    pub fn read<'a>(&'a self) -> &'a [u8] {
        let buf = unsafe { self.read_buf.as_mut_slice() };
        buf[0] = HEAD0;
        buf[1] = HEAD1;

        loop {
            self.wait_ready();
            let Some(len) = self.sync() else {
                continue;
            };
            if len < (MIN_LEN as usize) {
                continue;
            }

            buf[2] = len as u8;

            let Some(_) = self.recv_bytes(&mut buf[3..len]) else {
                continue;
            };

            let sum = xor_sum(&buf[3..(len - 2)]);
            if sum != buf[len - 2] {
                continue;
            }

            if END != buf[len - 1] {
                continue;
            }

            return &buf[..len];
        }
    }

    unsafe fn start_conn(&self) {
        self.thread.spawn(&ConnTask);
    }
}

static CTX: Context = Context::uninit();

pub unsafe fn start() {
    CTX.start_conn();
}

struct ConnTask;

impl Task for ConnTask {

    fn run(&self) {
        loop {
            let buf = CTX.read();
            let request = RequestRef::new(buf);
            let dst = request.dst();
            if dst != address::MY {
                cmd_can::write(buf);
                continue;
            }
            DISPATCHER.exec(request);
        }
    }
}
